 using System;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using Microsoft.Practices.RecipeFramework.VisualStudio.Library.Templates;
using Microsoft.Practices.RepositoryFactory.CodeGeneration.ObjectModel;
using Microsoft.VisualStudio.TextTemplating;
using NUnit.Framework;

namespace Microsoft.Practices.RepositoryFactory.Tests.Templates
{
    /// <summary>
    /// This class contains methods to test the text template files (.t4).
    /// </summary>
    public abstract class T4ProcessingFixture
    {
        /// <summary>
        /// Concrete classes must define the provider connection setting using the DatabaseProvider class.
        /// For example, this property can return DatabaseProvider.SqlServer for a subclass testing for MS SQL server.
        /// </summary>
        public abstract string ConnectionSettingProvider { get; }

        [Test]
        public void BusinessEntityCS()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\BE.CS\BusinessEntity.cs.t4");
        }

        [Test]
        public void BusinessEntityVB()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\BE.VB\BusinessEntity.vb.t4");
        }

        [Test]
        public void EntityDeleteFactoryCS()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.CS\EntityDeleteFactory.cs.t4");
        }

        [Test]
        public void EntityDeleteFactoryVB()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.VB\EntityDeleteFactory.vb.t4");
        }

        [Test]
        public void EntityFactoryCS()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.CS\EntityFactory.cs.t4");
        }

        [Test]
        public void EntityFactoryVB()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.VB\EntityFactory.vb.t4");
        }

        [Test]
        public void EntityInsertFactoryCS()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.CS\EntityInsertFactory.cs.t4");
        }

        [Test]
        public void EntityBulkInsertFactoryCS()
        {
           IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
           ProcessT4(arguments, @"Templates\T4\DAC.CS\EntityBulkInsertFactory.cs.t4");
        }

        [Test]
        public void EntityBulkUpdateFactoryCS()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.CS\EntityBulkUpdateFactory.cs.t4");
        }

        [Test]
        public void EntityInsertFactoryVB()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.VB\EntityInsertFactory.vb.t4");
        }

        [Test]
        public void EntityRepositoryCS()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.CS\EntityRepository.cs.t4");
        }

        [Test]
        public void EntityRepositoryForEachOperationCS()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.CS\EntityRepository.ForEachOperation.cs.t4");
        }

        [Test]
        public void EntityRepositoryVB()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.VB\EntityRepository.vb.t4");
        }

        [Test]
        public void EntityRepositoryInterfaceCS()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.CS\EntityRepositoryInterface.cs.t4");
        }

        [Test]
        public void EntityRepositoryInterfaceVB()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.VB\EntityRepositoryInterface.vb.t4");
        }

        [Test]
        public void EntitySelectionFactoryCS()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.CS\EntitySelectionFactory.cs.t4");
        }

        [Test]
        public void EntitySelectionFactoryVB()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.VB\EntitySelectionFactory.vb.t4");
        }

        [Test]
        public void EntityUpdateFactoryCS()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.CS\EntityUpdateFactory.cs.t4");
        }

        [Test]
        public void EntityUpdateFactoryVB()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.VB\EntityUpdateFactory.vb.t4");
        }

        [Test]
        public void IdentityCS()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.CS\Identity.cs.t4");
        }

        [Test]
        public void IdentityVB()
        {
            IDictionary<string, PropertyData> arguments = BuildDefaultT4ArgumentList();
            ProcessT4(arguments, @"Templates\T4\DAC.VB\Identity.vb.t4");
        }

        [Test]
        public void StoredProcedures()
        {
            IDictionary<string, PropertyData> arguments = new Dictionary<string, PropertyData>();
            ArrayList tableStoredProcedureMappings = new ArrayList();
            arguments.Add("TableStoredProcedureMappings", new PropertyData(tableStoredProcedureMappings, typeof(ArrayList)));
            ProcessT4(arguments, @"Templates\T4\SP\SqlServer\StoredProcedures.t4");
        }

        /// <summary>
        /// Test the given T4 file for compiling errors.
        /// </summary>
        /// <param name="arguments">List of arguments to be passed to the t4 file.</param>
        /// <param name="templateFileName">T4 filename including relative path from the location of the DataAccess Guidance Package project.
        /// For example, @"Templates\T4\SP\SqlServer\StoredProcedures.t4"</param>
        private static void ProcessT4(IDictionary<string, PropertyData> arguments, string templateFileName)
        {
            string rootDir = Path.Combine(Directory.GetCurrentDirectory(), @"..\..\..\Data Access Guidance Package");
            TemplateHost host = new TemplateHost(rootDir, arguments);

            Assert.IsTrue(File.Exists(templateFileName), "Cannot find " + templateFileName);
            host.TemplateFile = templateFileName;

            Engine engine = new Engine();

            //Read the text template.
            string input = File.ReadAllText(templateFileName);

            //Transform the text template.
            string output = engine.ProcessTemplate(input, host);

            // Check for compiling errors.
            if (host.Errors.HasErrors)
            {
                StringBuilder sb = new StringBuilder();
                foreach (CompilerError error in host.Errors)
                {
                    if (!error.IsWarning)
                        sb.AppendLine(error.ErrorText);
                }
                Assert.IsFalse(host.Errors.HasErrors, "There are compiling errors:\n\n" + sb);
            }

            // No compiling errors but the output file is empty.
            Assert.IsNotEmpty(output, "Generated file from T4 is empty.");

            return;
        }

        /// <summary>
        /// Builds the default argument list (t4 property list) to be used when running the t4 transformation.
        /// </summary>
        private IDictionary<string, PropertyData> BuildDefaultT4ArgumentList()
        {
            // Populates the entities ArrayList to be used when running the t4's.
            ArrayList entities = new ArrayList();

            // Create a new entity
            Entity entity = new Entity();
            entity.Name = "Customer";
            entity.Namespace = "BusinessEntities.Customers";
            entity.Properties = new List<Property>();

            // Create properties for the new entity
            Property property = new Property();
            property.Name = "CustomerId";
            property.Type = typeof(Int32);
            property.ColumnName = "CustomerId";
            property.IsNullable = false;
            property.IsPrimaryKey = true;
            property.ReadOnly = false;
            entity.Properties.Add(property);

            property = new Property();
            property.Name = "CustomerName";
            property.Type = typeof(String);
            property.ColumnName = "CustomerName";
            property.IsNullable = true;
            property.IsPrimaryKey = false;
            property.ReadOnly = false;
            entity.Properties.Add(property);

            entities.Add(entity);

            // Build the T4 argument list.
            IDictionary<string, PropertyData> arguments = new Dictionary<string, PropertyData>();
            arguments["Entities"] = new PropertyData(entities, typeof(ArrayList));
            arguments["BusinessEntityClassNamespace"] = new PropertyData("MyApplication.BusinessEntities", typeof(string));
            arguments["ParentNamespace"] = new PropertyData("MyApplication", typeof(string));
            arguments["DataAccessClassNamespace"] = new PropertyData("DataAccessClassNamespace", typeof(string));
            arguments["InterfacesNamespace"] = new PropertyData("InterfacesNamespace", typeof(string));
            arguments["ConnectionSettingProvider"] = new PropertyData(ConnectionSettingProvider, typeof(string));

            return arguments;
        }
    }
}
